#pragma once

#include "Thread_mutex.hpp"
#include <queue>

using namespace std;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t productor = PTHREAD_COND_INITIALIZER;
pthread_cond_t consumer = PTHREAD_COND_INITIALIZER;

template<class T>
class BlockQueue
{
public:

    BlockQueue(){}

    BlockQueue(int data)
        :_data(data)
    {}

    bool IsFull()
    {
        return que.size() == _data;            
    }

    bool IsNull()
    {
        return que.empty();
    }

    void Push(T& in)
    {
        pthread_mutex_lock(&mutex);
        while(IsFull())
        {
            pwait++;
            pthread_cond_wait(&productor, &mutex);
            pwait--;
        }
      
        
        que.push(in);

        if(cwait > 0)
            pthread_cond_signal(&consumer);

        pthread_mutex_unlock(&mutex);
    }

    void Pop(T& out)
    {
        pthread_mutex_lock(&mutex);
        while(IsNull())
        {
            cwait++;
            pthread_cond_wait(&consumer, &mutex);
            cwait--;
        }

   
        out = que.front();
        que.pop();

        if(pwait > 0)
            pthread_cond_signal(&productor);     

        pthread_mutex_unlock(&mutex);  
    }

private:
    int _data;
    queue<T> que;

    int pwait = 0;
    int cwait = 0;
};